# myTeam.py
# ---------
# Licensing Information: Please do not distribute or publish solutions to this
# project. You are free to use and extend these projects for educational
# purposes. The Pacman AI projects were developed at UC Berkeley, primarily by
# John DeNero (denero@cs.berkeley.edu) and Dan Klein (klein@cs.berkeley.edu).
# For more info, see http://inst.eecs.berkeley.edu/~cs188/sp09/pacman.html

from captureAgents import CaptureAgent
import random, time, util
from game import Directions
import game
from util import nearestPoint

#################
# Team creation #
#################

def createTeam(firstIndex, secondIndex, isRed,
               first = 'SwarmAgent', second = 'SwarmAgent'):
  """
  This function should return a list of two agents that will form the
  team, initialized using firstIndex and secondIndex as their agent
  index numbers.  isRed is True if the red team is being created, and
  will be False if the blue team is being created.

  As a potentially helpful development aid, this function can take
  additional string-valued keyword arguments ("first" and "second" are
  such arguments in the case of this function), which will come from
  the --redOpts and --blueOpts command-line arguments to capture.py.
  For the nightly contest, however, your team will be created without
  any extra arguments, so you should make sure that the default
  behavior is what you want for the nightly contest.
  """

  # The following line is an example only; feel free to change it.
  return [eval(first)(firstIndex), eval(second)(secondIndex)]

##########
# Agents #
##########

def directionToPosition(dir, pos):
    if (dir == Directions.NORTH):
         return [pos[0], pos[1] + 1]
    elif (dir == Directions.EAST):
        return [pos[0] + 1, pos[1]]
    elif (dir == Directions.SOUTH):
        return [pos[0], pos[1] - 1]
    elif (dir == Directions.WEST):
        return [pos[0] - 1, pos[1]]
    else:
        return pos
    
def positionToDirection(initPos, finalPos):
      if (finalPos[1] > initPos[1]):
          return Directions.NORTH
      elif (finalPos[0] > initPos[0]):
          return Directions.EAST
      elif (finalPos[1] < initPos[1]):
          return Directions.SOUTH
      elif (finalPos[0] < initPos[0]):
          return Directions.WEST
      else:
          return Directions.STOP
      
def closestFoodDistance(pacman, foods, walls):
    if walls in foods:
        return 0;
    visited = (pacman,)
    queue = util.PriorityQueue()
    queue.push((pacman, 1), 1)
    while not queue.isEmpty():
        current, distance = queue.pop()
        if current in foods:
            return distance
        for pos in [(current[0] + 1, current[1]), (current[0] - 1, current[1]), (current[0], current[1] + 1), (current[0], current[1] - 1)]:
            if (not pos in walls) and (not pos in visited):
                queue.push((pos, distance + 1), distance + 1)
                visited += (pos,)
    return 1

class DummyAgent(CaptureAgent):
  """
  A Dummy agent to serve as an example of the necessary agent structure.
  You should look at baselineTeam.py for more details about how to
  create an agent as this is the bare minimum.
  """
  def registerInitialState(self, gameState):
    CaptureAgent.registerInitialState(self, gameState)

  def chooseAction(self, gameState):
    actions = gameState.getLegalActions(self.index)
    self.history.append(gameState.getAgentState(self.index).getPosition())
    
    return random.choice(actions)


class StoppedAgent(CaptureAgent):
  """
  A Dummy agent to serve as an example of the necessary agent structure.
  You should look at baselineTeam.py for more details about how to
  create an agent as this is the bare minimum.
  """   
  
  def registerInitialState(self, gameState):
    CaptureAgent.registerInitialState(self, gameState)

  def chooseAction(self, gameState):
      return Directions.STOP

class SwarmAgent(CaptureAgent):
  """
  A Dummy agent to serve as an example of the necessary agent structure.
  You should look at baselineTeam.py for more details about how to
  create an agent as this is the bare minimum.
  """ 
  def __init__(self, index, timeForComputing = .1):
      CaptureAgent.__init__(self, index)
      self.pheromoneMatrix = []
      self.foodConsumed = 0
      #JULIA added field   
  
  
  def registerInitialState(self, gameState):
    CaptureAgent.registerInitialState(self, gameState)

  def chooseAction(self, gameState):
      import random
      import sys
      actions = gameState.getLegalActions(self.index)
      
      self.history.append(gameState.getAgentState(self.index).getPosition())          
      position = gameState.getAgentState(self.index).getPosition()
      positions = map(lambda x: directionToPosition(x, position), actions)
      #JULIA changed eval function
      evals = map(lambda x: self.evaluate2(gameState, x), actions)
      #print "^^^^^ "+ str(evals)
      pheromones = map(lambda x: self.pheromoneMatrix[int(x[0])][int(x[1])].pheromoneForIndex(self.index // 2), positions)
      
      #lazy
      if self.index == 0: 
          otherIndex = 1
      else: 
          otherIndex = 0
      weakPheromones = map(lambda x: self.pheromoneMatrix[int(x[0])][int(x[1])].pheromoneForIndex(otherIndex), positions)
      
      max = pheromones[0]# + evals[0]
      maxIndex = 0
      benefits = []
      maxBenefit = sys.float_info.max/4
      sum = 0;
      for i in range(len(actions)): 
          if ( evals[i] < 0 ):
              #death
              benefit = 0
          else:
              try:
                myPhero = pheromones[i] + 0.1 * weakPheromones[i]
                #TODO TEST
                #benefit = pow( myPhero,6) * pow(evals[i], 9)
                benefit = pow( myPhero,6) * pow(evals[i], 9)
                #benefit = pow(benefit,3) * pow(1.2, evals[i])
                #benefit = pow(benefit+1,2) * pow(evals[i], 3)
              except OverflowError:
                print "***failure " + str(benefit) + " eval: " + str(evals[i]) + " index " +str(self.index)
                benefit = maxBenefit
              if benefit > maxBenefit:
                benefit = maxBenefit
          benefits.append( benefit )
          sum += benefit  
      
      if (sum == 0):
          print "FAILURE: sum == 0!!!!!!!!!!"
          print actions
          return actions[ random.randint(0, len(actions)-1) ]

      selection = random.random() * sum
      temp = selection
      for i in range( len(benefits) ):
          selection -= benefits[i]
          if ( selection <= 0 ):      
#              print gameState.getAgentState(self.index).getPosition(),
              successor = self.getSuccessor(gameState, actions[i])
#              print self.getFood(self.getSuccessor(gameState, actions[i])).asList()

              #JULIA foodConsumed tracked by each agent
              if (successor.getAgentState(self.index).getPosition() 
                  in self.getFood( gameState ).asList() ):
                  #print "agent " + str(self.index) + " eats"
                  self.foodConsumed += 1
     
              return actions[i]

      print "failed" + str(self.index)  + "sel: " + str(temp) + "sum: " + str(sum) + " bene: " +str(benefits)

      return Directions.STOP     
        
  def getSuccessor(self, gameState, action):
    """
    Finds the next successor which is a grid position (location tuple).
    """
    successor = gameState.generateSuccessor(self.index, action)
    pos = successor.getAgentState(self.index).getPosition()
    if pos != nearestPoint(pos):
      # Only half a grid position was covered
      return successor.generateSuccessor(self.index, action)
    else:
      return successor

#    evaluate2
#     gives an eval >= 0. Negative 1 if death

  def evaluate2(self, gameState, action):
     
    eval = 0
      
    successor = self.getSuccessor(gameState, action)  
    foodList = self.getFood(successor).asList() 
         
    if len(foodList) > 0: # This should always be True,  but better safe than sorry
      myPos = successor.getAgentState(self.index).getPosition()
      minDistance = min([self.getMazeDistance(myPos, food) for food in foodList])
    eval += 1 / float(minDistance)
    
    if ( len(foodList) < len(self.getFood(gameState).asList()) ):
        eval += 1
      
    powerUps = self.getCapsules(successor)
    if ( successor.getAgentState(self.index).getPosition() in powerUps ):
        eval += 10
      
    #will pacman die
    myState = successor.getAgentState(self.index)
    enemies = [gameState.getAgentState(i) for i in self.getOpponents(gameState)]
    enemyPac = [a for a in enemies if a.isPacman and a.getPosition() != None]
    enemyGho = [a for a in enemies if a.isPacman and a.getPosition() != None]
    #enemyDists = [self.getMazeDistance(myPos, enemy.getPosition()) for enemy in enemies]
    enemyGhostDists = [self.getMazeDistance(myPos, enemy.getPosition()) for enemy in enemyGho]
    enemyPacDists = [self.getMazeDistance(myPos, enemy.getPosition()) for enemy in enemyPac]
    
    
    if ( myState.isPacman and len(enemyGhostDists) != 0 ):
        closestEnemyDist = min(enemyGhostDists)
        if enemyGho[0].scaredTimer>0:
          #scared
          if closestEnemyDist ==0:
            eval += 15
          if closestEnemyDist < 2:
            eval += 10 / float(closestEnemyDist)
        else:
          if closestEnemyDist <= 2:
            return -1   
#          elif closestEnemyDist <= 4:
#              eval = eval * 0.6  

    elif (  not(myState.isPacman) and len(enemyPacDists) != 0 ):
        # I am a ghost case
        closestEnemyDist = min(enemyPacDists)
        if myState.scaredTimer>0:
          #scared
          if closestEnemyDist < 2:
            return -1
#          elif closestEnemyDist <= 4:
#              eval = eval * 0.6  
        else:
          if closestEnemyDist < 0.1:
            eval += 15
          elif (closestEnemyDist <= 2):
            eval += 10 / float(closestEnemyDist) 
    
    #reduce cycling
    if (action == Directions.STOP):
        eval *= 0.5
        
    eval = eval / float( self.history.count( myPos ) + 1 )
    #if ( len(self.history) > 1 and self.history[-2] == myPos ):
    #    eval *= 0.6
    
    return eval

  def evaluate(self, gameState, action):
    """
    Computes a linear combination of features and feature weights
    """
    features = self.getFeatures(gameState, action)
    weights = self.getWeights(gameState, action)
    return features * weights

  def getFeatures(self, gameState, action):
    features = util.Counter()
    successor = self.getSuccessor(gameState, action)
    features['successorScore'] = self.getScore(successor)

    # Compute distance to the nearest food
    foodList = self.getFood(successor).asList()
    if len(foodList) > 0: # This should always be True,  but better safe than sorry
      myPos = successor.getAgentState(self.index).getPosition()
      minDistance = min([self.getMazeDistance(myPos, food) for food in foodList])
      features['distanceToFood'] = minDistance
    return features

  def getWeights(self, gameState, action):
    return {'successorScore': 100, 'distanceToFood': -1}

   
  def setPheromoneMatrix(self, matrix):
    self.pheromoneMatrix = matrix
  
  
    
class EliteSwarmAgent(SwarmAgent):  
    
  def chooseAction(self, gameState):
      import random
      actions = gameState.getLegalActions(self.index)\
      #stop not allowed
      actions.remove(Directions.STOP)
      self.history.append(gameState.getAgentState(self.index).getPosition())          
      position = gameState.getAgentState(self.index).getPosition()
      positions = map(lambda x: directionToPosition(x, position), actions)
      #JULIA changed eval function
      evals = map(lambda x: self.evaluate2(gameState, x), actions)
      #print "^^^^^ "+ str(evals)
     
      
      pheromones = map(lambda x: self.pheromoneMatrix[int(x[0])][int(x[1])].pheromoneForIndex(self.index // 2), positions)
      #lazy
      if self.index == 0: 
          otherIndex = 1
      else: 
          otherIndex = 0
      weakPheromones = map(lambda x: self.pheromoneMatrix[int(x[0])][int(x[1])].pheromoneForIndex(otherIndex), positions)      
      
      max = pheromones[0]# + evals[0]
      maxIndex = 0
      benefits = []
      maxBenefit = -1
      maxIndices = []
      
      sum = 0;
      for i in range(len(actions)): 
          if ( evals[i] < 0 ):
              #death
              benefit = 0
          else:
              #benefit = pheromones[i]
              myPhero = pheromones[i] + 0.1 * weakPheromones[i]
              #TODO TEST
              #benefit = pow( myPhero,6) * pow(evals[i], 9)
              benefit = pow( myPhero,6) * pow(evals[i], 9)
              successor = self.getSuccessor(gameState, actions[i])
              nextPos = successor.getAgentState(self.index).getPosition()  
              #print self.history.count( nextPos ) 
              benefit = benefit / float( self.history.count( nextPos ) + 1 )
              
          if ( benefit > maxBenefit):
              maxIndices = [i]
              maxBenefit = benefit
          elif ( benefit == maxBenefit ):
              maxIndices.append(i)
      
      index = random.randint(0, len(maxIndices)-1)
      selectedAction = actions[ maxIndices[index] ]
      successor = self.getSuccessor(gameState, selectedAction)        
      if (successor.getAgentState(self.index).getPosition() 
          in self.getFood( gameState ).asList() ):
          #print "agent " + str(self.index) + " eats"
          self.foodConsumed += 1

      return selectedAction 
    
      